import { getCode } from '@/utils';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import dayjs from 'dayjs';
import { JvmService } from './jvm.service';

export interface YopOrderDto {
  orderId: string;
  orderAmount: number;
  goodsName: string;
  returnUrl: string;
  notifyUrl: string;
  fundProcessType?: 'REAL_TIME' | 'DELAY_SETTLE' | 'REAL_TIME_DIVIDE';
  divideDetail?: any[];
  businessInfo?: any;
}

export interface YopWalletDto {
  requestNo: string;
  userNo: string;
  mobile: string;
  name: string;
  idCard: string;
  returnUrl: string;
  notifyUrl: string;
}

@Injectable()
export class YopService {
  private readonly publicKey: string;
  private readonly privateKey: string;
  private readonly appKey: string;
  private readonly merCustId: string;
  private readonly notifyUrl: string;
  private readonly returnUrl: string;
  private readonly yop: any;
  private readonly YopRequest: any;
  private readonly YopRsaClient: any;
  constructor(
    private readonly config: ConfigService,
    private readonly jvmService: JvmService,
  ) {
    this.publicKey = this.config.get('yop.publicKey');
    this.privateKey = this.config.get('yop.privateKey');
    this.appKey = this.config.get('yop.appKey');
    this.merCustId = this.config.get('yop.merCustId');
    this.notifyUrl = this.config.get('yop.notifyUrl');
    this.returnUrl = this.config.get('yop.returnUrl');
    this.YopRequest = this.jvmService.import(
      'com.yeepay.g3.sdk.yop.client.YopRequest',
    );
    this.YopRsaClient = this.jvmService.import(
      'com.yeepay.g3.sdk.yop.client.YopRsaClient',
    );
    this.yop = this.jvmService.import('com.openx.Yop');
  }

  get NotifyUrl() {
    return this.notifyUrl;
  }

  get ReturnUrl() {
    return this.returnUrl;
  }

  async order({
    orderId,
    orderAmount,
    goodsName,
    returnUrl,
    notifyUrl,
    fundProcessType = 'REAL_TIME',
    divideDetail = null,
    businessInfo = null,
  }: YopOrderDto): Promise<any> {
    return new Promise(async (r) => {
      const request = new this.YopRequest(this.appKey, this.privateKey);
      request.addParamSync('parentMerchantNo', this.merCustId);
      request.addParamSync('merchantNo', this.merCustId);
      request.addParamSync('orderId', orderId);
      request.addParamSync('orderAmount', orderAmount);
      request.addParamSync('goodsName', goodsName);
      request.addParamSync('returnUrl', returnUrl);
      request.addParamSync('notifyUrl', notifyUrl);
      request.addParamSync('fundProcessType', fundProcessType);
      if (fundProcessType === 'REAL_TIME_DIVIDE' && !!divideDetail) {
        request.addParamSync('divideDetail', JSON.stringify(divideDetail));
      }
      if (!!businessInfo) {
        request.addParamSync('businessInfo', JSON.stringify(businessInfo));
      }

      const response = this.YopRsaClient.postSync(
        '/rest/v1.0/cashier/unified/order',
        request,
      );

      r(this.toJSON(response));
    });
  }

  async orderQuery(orderId: string): Promise<any> {
    return new Promise(async (r) => {
      const request = new this.YopRequest(this.appKey, this.privateKey);
      request.addParamSync('parentMerchantNo', this.merCustId);
      request.addParamSync('merchantNo', this.merCustId);
      request.addParamSync('orderId', orderId);

      const response = this.YopRsaClient.getSync(
        '/rest/v1.0/trade/order/query',
        request,
      );

      r(this.toJSON(response));
    });
  }

  async wallet({
    requestNo,
    userNo,
    mobile,
    name,
    idCard,
    returnUrl,
    notifyUrl,
  }: YopWalletDto): Promise<any> {
    return new Promise(async (r) => {
      const request = new this.YopRequest(this.appKey, this.privateKey);
      request.addParamSync('parentMerchantNo', this.merCustId);
      request.addParamSync('merchantNo', this.merCustId);
      request.addParamSync('merchantUserNo', userNo);
      request.addParamSync('name', name);
      request.addParamSync('certificateType', 'IDENTITY_CARD');
      request.addParamSync('certificateNo', idCard);
      request.addParamSync('mobile', mobile);
      request.addParamSync('requestNo', requestNo);
      request.addParamSync('returnUrl', returnUrl);
      request.addParamSync('notifyUrl', notifyUrl);

      const response = this.YopRsaClient.postSync(
        '/rest/v2.0/m-wallet/wallet/index',
        request,
      );

      r(this.toJSON(response));
    });
  }

  async memberQuery(id: string): Promise<any> {
    return new Promise(async (r) => {
      const request = new this.YopRequest(this.appKey, this.privateKey);
      request.addParamSync('parentMerchantNo', this.merCustId);
      request.addParamSync('merchantNo', this.merCustId);
      request.addParamSync('externalUserId', id);

      const response = this.YopRsaClient.getSync(
        '/rest/v1.0/m-wallet/member/query',
        request,
      );

      r(this.toJSON(response));
    });
  }

  async verifySign(response: string): Promise<any> {
    return new Promise((r) =>
      r(
        JSON.parse(
          this.yop.verifySignSync(response, this.publicKey, this.privateKey),
        ),
      ),
    );
  }

  async generateOrderNo() {
    return `${dayjs().format('YYYYMMDDHHmmss')}${await getCode(11)}`;
  }

  private toJSON(response: any) {
    console.log(`\n ${response?.toStringSync()} \n`);
    if (!response.isSuccessSync()) {
      const error = response.getErrorSync();
      return {
        isSuccess: false,
        state: response.getStateSync(),
        requestId: response.getRequestIdSync(),
        error: {
          code: error?.getCodeSync(),
          message: error?.getMessageSync(),
        },
      };
    } else {
      const result = response.getStringResultSync();
      return {
        isSuccess: true,
        state: response.getStateSync(),
        requestId: response.getRequestIdSync(),
        result: result ? JSON.parse(result) : null,
      };
    }
  }
}
